package com.cooksys.film_rental.controllers;

import java.security.Principal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;

import com.cooksys.film_rental.bo.ActorBo;
import com.cooksys.film_rental.bo.CategoryBo;
import com.cooksys.film_rental.bo.CountryBo;
import com.cooksys.film_rental.bo.CustomerBo;
import com.cooksys.film_rental.bo.FilmBo;
import com.cooksys.film_rental.bo.InventoryBo;
import com.cooksys.film_rental.bo.LanguageBo;
import com.cooksys.film_rental.bo.ProfileFormBo;
import com.cooksys.film_rental.bo.RentalBo;
import com.cooksys.film_rental.bo.StoreBo;
import com.cooksys.film_rental.bo.UserBo;
import com.cooksys.film_rental.model.Actor;
import com.cooksys.film_rental.model.Category;
import com.cooksys.film_rental.model.Country;
import com.cooksys.film_rental.model.Customer;
import com.cooksys.film_rental.model.Film;
import com.cooksys.film_rental.model.Inventory;
import com.cooksys.film_rental.model.Language;
import com.cooksys.film_rental.model.Rental;
import com.cooksys.film_rental.model.Store;
import com.cooksys.film_rental.model.User;
import com.cooksys.film_rental.model.forms.AccountForm;
import com.cooksys.film_rental.model.forms.AdvancedFilmSearchForm;
import com.cooksys.film_rental.model.forms.CartForm;
import com.cooksys.film_rental.model.forms.CheckoutForm;
import com.cooksys.film_rental.model.forms.FilmSearchForm;
import com.cooksys.film_rental.model.forms.ProfileForm;

@Controller
@RequestMapping(value = "/user")
@SessionAttributes({ "user", "customer", "checkoutForm", "cartForm", "filmSearchForm",
		"advancedFilmSearchForm", "categoryMap", "actorMap", "languageMap",
		"ratingMap", "runtimeMap" })
public class UserController {

	@Autowired
	UserBo userBo;

	@Autowired
	CustomerBo customerBo;

	@Autowired
	ProfileFormBo profileFormBo;

	@Autowired
	StoreBo storeBo;

	@Autowired
	CountryBo countryBo;

	@Autowired
	FilmBo filmBo;

	@Autowired
	CategoryBo categoryBo;

	@Autowired
	ActorBo actorBo;

	@Autowired
	LanguageBo languageBo;

	@Autowired
	InventoryBo inventoryBo;

	@Autowired
	RentalBo rentalBo;

	@RequestMapping(method = RequestMethod.GET)
	public String userRedirect() {
		return "userRedirectView";
	}

	@RequestMapping(value = "/home", method = RequestMethod.GET)
	public String userHome() {
		return "loggedInHomeView";
	}
	
	@RequestMapping(value = "/films", method = RequestMethod.GET)
	public String films(){
		return "loggedinFilmSearchView";
	}
	
	@RequestMapping(value = "/films/listings", method = RequestMethod.GET)
	public String filmListing(@RequestParam("id") Short filmId, ModelMap model) {
		
		Film film = filmBo.getFilmByFilmId(filmId);
		model.addAttribute("film", film);
		model.addAttribute("categoryList", categoryBo.searchCategoriesByFilm(film));
		model.addAttribute("actorList", actorBo.searchActorsByFilm(film));
		
		return "loggedinFilmListingView";
	}

	@RequestMapping(value = "/profile", method = RequestMethod.GET)
	public String profile(@ModelAttribute("customer") Customer customer,
			ModelMap model) {

		ProfileForm profileForm = profileFormBo
				.populateProfileFormByCustomer(customer);

		model.addAttribute("profileForm", profileForm);

		return "profileView";

	}

	@RequestMapping(value = "/profile/update", method = RequestMethod.POST)
	public String updateProfile(
			@ModelAttribute("profileForm") @Valid ProfileForm profileForm,
			BindingResult result, ModelMap model) {

		if (!result.hasErrors()) {

			Customer customer = (Customer) model.get("customer");

			customer = profileFormBo.updateCustomerFromProfileForm(profileForm);

			model.addAttribute("customer", customer);

		}

		return "profileView";

	}

	@RequestMapping(value = "/search", method = RequestMethod.GET)
	public String userFilmSearchPage() {

		return "loggedinFilmSearchView";

	}

	@RequestMapping(value = "/search", method = RequestMethod.POST)
	public String userFilmSearch(
			@ModelAttribute("filmSearchForm") @Valid FilmSearchForm filmSearchForm,
			BindingResult result, ModelMap model) {

		if (result.hasErrors()) {
			return "loggedinFilmSearchView";
		}

		List<Film> searchResults = filmBo
				.searchFilmsByPartialTitle(filmSearchForm.getSearchString());

		model.addAttribute("searchResults", searchResults);

		return "loggedinSearchResultsView";

	}

	@RequestMapping(value = "/search/advanced", method = RequestMethod.POST)
	public String userFilmSearch(
			@ModelAttribute("advancedFilmSearchForm") @Valid AdvancedFilmSearchForm advancedFilmSearchForm,
			BindingResult result, ModelMap model) {

		if (result.hasErrors()) {
			return "loggedinFilmSearchView";
		}

		String partialTitle = advancedFilmSearchForm.getSearchString();
		Category categoryFilter = categoryBo
				.getCategoryByCategoryId(advancedFilmSearchForm.getCategoryId());
		Actor actorFilter = actorBo.getActorbyActorId(advancedFilmSearchForm
				.getActorId());
		Language languageFilter = languageBo
				.getLanguageByLanguageId(advancedFilmSearchForm.getLanguageId());
		Language orginalLanguageFilter = languageBo
				.getLanguageByLanguageId(advancedFilmSearchForm
						.getOriginalLanguageId());
		String ratingFilter = advancedFilmSearchForm.getRating();
		String runtimeFilter = advancedFilmSearchForm.getRuntime();

		List<Film> searchResults = filmBo.advancedSearch(partialTitle,
				categoryFilter, actorFilter, languageFilter,
				orginalLanguageFilter, ratingFilter, runtimeFilter);

		model.addAttribute("searchResults", searchResults);

		return "loggedinSearchResultsView";

	}

	@RequestMapping(value = "/checkout", method = RequestMethod.POST)
	public String userCheckout(
			@ModelAttribute("checkoutForm") CheckoutForm checkoutForm,
			@ModelAttribute("customer") Customer customer, ModelMap model) {

		Store customerStore = storeBo.getStoreByCustomer(customer);

		List<Film> filmsCheckedout = new ArrayList<Film>();
		List<Film> filmsNotCheckedout = new ArrayList<Film>();

		for (String filmId : checkoutForm.getFilmsToCheckout()) {
			Film filmToCheckout = filmBo.getFilmByFilmId(filmId);
			boolean available = filmBo.getFilmAvailabilityByStore(
					filmToCheckout, customerStore);

			if (available) {
				filmsCheckedout.add(filmToCheckout);
			} else {
				filmsNotCheckedout.add(filmToCheckout);
			}
		}

		for (Film f : filmsCheckedout) {
			Inventory inventoryToCheckout = inventoryBo
					.getInventoryByFilmAndStore(f, customerStore);
			if (inventoryToCheckout == null) {
				filmsNotCheckedout.add(f);
			} else {

				rentalBo.rentInventory(customer, inventoryToCheckout);

			}
		}

		model.addAttribute("unavailableFilms", filmsNotCheckedout);

		return "cartRedirectView";
	}

	@RequestMapping(value = "/cart", method = RequestMethod.GET)
	public String userCart(@ModelAttribute("customer") Customer customer,
			@ModelAttribute("rentalsInCart") List<Rental> rentalsInCart,
			ModelMap model) {

		rentalsInCart = customerBo.getRentalsInCart(customer);

		model.addAttribute("rentalsInCart", rentalsInCart);

		return "cartView";

	}

	@RequestMapping(value = "/cart/remove", method = RequestMethod.GET)
	public String userCartRemove(@ModelAttribute("customer") Customer customer,
			@RequestParam("id") Integer rentalId, ModelMap model) {

		Rental rentalToRemove = rentalBo.getRentalByRentalId(rentalId);

		rentalBo.delete(rentalToRemove);

		return "cartRedirectView";

	}

	@RequestMapping(value = "/cart/rent", method = RequestMethod.POST)
	public String userCartRent(@ModelAttribute("customer") Customer customer,
			@ModelAttribute("cartForm") CartForm cartForm, ModelMap model) {

		for (String rentalId : cartForm.getRentalsToPay()) {
			Rental rentalToPay = rentalBo.getRentalByRentalId(rentalId);

			rentalBo.payRental(rentalToPay, customer);
		}

		return "accountRedirectView";

	}

	@RequestMapping(value = "/account", method = RequestMethod.GET)
	public String userAccount(@ModelAttribute("customer") Customer customer,
			@ModelAttribute("paidRentals") List<Rental> paidRentals,
			ModelMap model) {

		paidRentals = customerBo.getPaidRentals(customer);

		model.addAttribute("paidRentals", paidRentals);

		return "accountView";

	}

	@RequestMapping(value = "/account/return", method = RequestMethod.POST)
	public String userAccountReturn(
			@ModelAttribute("accountForm") AccountForm accountForm,
			ModelMap model) {

		for(String rentalId : accountForm.getRentalsToReturn()){
			Rental rentalToReturn = rentalBo.getRentalByRentalId(rentalId);
			
			rentalBo.returnRental(rentalToReturn);
		}

		return "accountRedirectView";

	}
	
	@RequestMapping(value = "/logout", method = RequestMethod.GET)
	public String userLogout(@ModelAttribute("customer") Customer customer){
		
		List<Rental> rentalsInCart = customerBo.getRentalsInCart(customer);
		
		for(Rental r : rentalsInCart){
			rentalBo.delete(r);
		}
		
		return "logoutRedirectView";
		
	}

	@ModelAttribute("user")
	private User initUser(Principal principal) {
		User user = new User();
		user = userBo.getUserByUserId(principal.getName());
		return user;
	}

	@ModelAttribute("customer")
	private Customer initCustomer(@ModelAttribute("user") User user) {
		Customer customer = new Customer();
		customer = user.getCustomer();
		return customer;
	}

	@ModelAttribute("cartForm")
	private CartForm initCartForm() {
		CartForm cartForm = new CartForm();
		return cartForm;
	}

	@ModelAttribute("checkoutForm")
	private CheckoutForm initCheckoutForm() {
		CheckoutForm checkoutForm = new CheckoutForm();
		return checkoutForm;
	}

	@ModelAttribute("filmSearchForm")
	private FilmSearchForm initFilmSearchForm() {
		FilmSearchForm filmSearchForm = new FilmSearchForm();
		return filmSearchForm;
	}

	@ModelAttribute("storeMap")
	private Map<String, String> populateStoreMap() {

		Map<String, String> storeIdToDisplayValueMapping = new LinkedHashMap<String, String>();

		List<Store> stores = storeBo.getAllStores();
		for (Store s : stores) {
			storeIdToDisplayValueMapping.put(s.getStoreId().toString(),
					"Store " + s.getStoreId());
		}

		return storeIdToDisplayValueMapping;

	}

	@ModelAttribute("countryMap")
	private Map<String, String> populateCountryMap() {

		Map<String, String> countryIdToDisplayValueMapping = new LinkedHashMap<String, String>();

		List<Country> countries = countryBo.getAllCountries();
		for (Country c : countries) {
			countryIdToDisplayValueMapping.put(c.getCountryId().toString(),
					c.getCountry());
		}

		return countryIdToDisplayValueMapping;

	}

	@ModelAttribute("profileForm")
	private ProfileForm initProfileForm(@ModelAttribute("user") User user,
			ModelMap model) {

		Customer customer = user.getCustomer();

		ProfileForm profileForm = profileFormBo
				.populateProfileFormByCustomer(customer);

		return profileForm;

	}

	@ModelAttribute("categoryMap")
	private Map<String, String> populateCategoryMap() {

		Map<String, String> categoryIdToDisplayValueMapping = new LinkedHashMap<String, String>();

		List<Category> categories = categoryBo.getAllCategories();

		categoryIdToDisplayValueMapping.put("0", "--- ALL ---");

		for (Category c : categories) {
			categoryIdToDisplayValueMapping.put(c.getCategoryId().toString(),
					c.getName());
		}

		return categoryIdToDisplayValueMapping;

	}

	@ModelAttribute("actorMap")
	private Map<String, String> populateActorMap() {

		Map<String, String> actorIdToDisplayValueMapping = new LinkedHashMap<String, String>();

		List<Actor> actors = actorBo.getAllActors();

		actorIdToDisplayValueMapping.put("0", "--- ALL ---");

		for (Actor a : actors) {
			actorIdToDisplayValueMapping.put(a.getActorId().toString(),
					a.getFirstName() + " " + a.getLastName());
		}

		return actorIdToDisplayValueMapping;

	}

	@ModelAttribute("languageMap")
	private Map<String, String> populateLanguageMap() {

		Map<String, String> languageIdToDisplayValueMapping = new LinkedHashMap<String, String>();

		List<Language> languages = languageBo.getAllLanguages();

		languageIdToDisplayValueMapping.put("0", "--- ALL ---");

		for (Language l : languages) {
			languageIdToDisplayValueMapping.put(l.getLanguageId().toString(),
					l.getName());
		}

		return languageIdToDisplayValueMapping;

	}

	@ModelAttribute("ratingMap")
	private Map<String, String> populateRatingMap() {

		Map<String, String> ratingKeyToDisplayValueMapping = new LinkedHashMap<String, String>();

		List<String> ratings = filmBo.getAllRatings();

		ratingKeyToDisplayValueMapping.put("ALL", "--- ALL ---");

		for (String r : ratings) {
			ratingKeyToDisplayValueMapping.put(r, r);
		}

		return ratingKeyToDisplayValueMapping;

	}

	@ModelAttribute("runtimeMap")
	private Map<String, String> populateRuntimeMap() {

		Map<String, String> runtimeKeyToDisplayValueMapping = new LinkedHashMap<String, String>();

		short maxRuntime = filmBo.getMaxRuntime();

		runtimeKeyToDisplayValueMapping.put("ALL", "--- ALL ---");

		for (short i = 0; i < maxRuntime + 10; i += 10) {

			runtimeKeyToDisplayValueMapping.put("" + i, "less than " + i
					+ " minutes");

		}

		return runtimeKeyToDisplayValueMapping;

	}

	@ModelAttribute("advancedFilmSearchForm")
	private AdvancedFilmSearchForm initAdvancedFilmSearchForm() {
		AdvancedFilmSearchForm advancedFilmSearchForm = new AdvancedFilmSearchForm();
		return advancedFilmSearchForm;

	}

	@ModelAttribute("unavailableFilms")
	private List<Film> initUnavailableFilms() {
		List<Film> unavailableFilms = new ArrayList<Film>();
		return unavailableFilms;
	}

	@ModelAttribute("rentalsInCart")
	private List<Rental> initRentalsInCart() {
		List<Rental> rentalsInCart = new ArrayList<Rental>();
		return rentalsInCart;
	}

	@ModelAttribute("paidRentals")
	private List<Rental> initRentedFilms() {
		List<Rental> paidRentals = new ArrayList<Rental>();
		return paidRentals;
	}

	@ModelAttribute("accountForm")
	private AccountForm initAccountForm() {
		AccountForm accountForm = new AccountForm();
		return accountForm;
	}

}
